﻿/*
	Copyright (C) 2019 Zongming Liu <1849059316@qq.com>

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with this program; if not, write to the Free Software Foundation, Inc.,
	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "Sprite.h"
#include "../utility/utility.h"
#include "../base/VertexType.h"
#include "../render/ProgramCache.h"
#include "../utility/MacroTool.h"
#include "../win32/ApplicationView.h"
#include "../render/CustomCommand.h"
#include "../render/ProgramStateCache.h"
#include "../render/Camera.h"
#include "../render/Texture2D.h"
#include "../node/SpriteFrame.h"
#include "../render/TextureCache.h"
#include "../base/GameWorld.h"
#include "../render/TrianglesCommand.h"
#include "../render/Material.h"
#include "../render/Texture2D.h"
#include "../render/IRender.h"
#include <memory>

namespace SkyEngine2d
{
	
	Sprite::Sprite(Node* parent /*= nullptr*/)
		:Node(parent)
		, m_vpMatrix()
		, m_texture(nullptr)
		, m_material(nullptr)
		, m_cmd(nullptr)
		, m_isFlipH(false)
		, m_isFlipV(false)
		, m_anchorPoint(0.5f, 0.5f)
	{
	
	}
	
	
	Sprite* Sprite::create(const std::shared_ptr<SpriteFrame>& sprite_frame, Node* parent /*= nullptr */)
	{
		auto s = makeNodePtr(parent);
		if (s && s->initWithSpriteFrame(sprite_frame))
		{
			return s;
		}
	
		return nullptr;
	}
	
	Sprite* Sprite::create(const std::wstring& file_name, Node* parent /*= nullptr*/)
	{
		auto s = makeNodePtr(parent);
		if (s->initWithFile(file_name))
		{
			return s;
		}
		return nullptr;
	}
	
	Sprite* Sprite::create(const std::shared_ptr<Texture2D>& texture2D, const Rect& des_rect_in_texture, Node* parent /*= nullptr */)
	{
		auto s = makeNodePtr(parent);
		if (s && s->initWithTexture(texture2D, des_rect_in_texture))
		{
			return s;
		}
		return nullptr;
	}
	
	Sprite* Sprite::create(const std::wstring texture_filename, const Rect& des_rect_in_texture, Node* parent /*= nullptr */)
	{
		auto s = makeNodePtr(parent);
		if (s && s->initWithFile(texture_filename, des_rect_in_texture))
		{
			return s;
		}
		return nullptr;
	}
	
	Sprite::~Sprite()
	{
	
	}
	
	bool Sprite::initWithFile(const std::wstring& file_name)
	{
		if (!Node::init())
		{
			return false;
		}
	
		m_texture = GameWorld::getInstance()->getTextureCache()->addImage(file_name);
		if (!m_texture)
		{
			return false;
		}
	
		this->setTextureCoords(m_texture->getContentRect(), &m_quad);
	
		auto r = m_texture->getContentRect();
		//float half_w = r.size.width / 2;
		//float half_h = r.size.height / 2;
	
		this->setVertexCoords(Rect(0, 0, r.size.width, r.size.height), &m_quad);
	
		this->updateRenderResource();
	
		return true;
	}
	
	bool Sprite::initWithFile(const std::wstring& file_name, const Rect& des_rect)
	{
		if (!Node::init())
		{
			return false;
		}
		m_texture = GameWorld::getInstance()->getTextureCache()->addImage(file_name);
		if (!m_texture)
		{
			return false;
		}
	
		this->setTextureCoords(des_rect, &m_quad);
	
		this->setVertexCoords(Rect(0, 0, des_rect.size.width, des_rect.size.height), &m_quad);
	
		this->updateRenderResource();
	
		return true;
	}
	
	bool Sprite::initWithSpriteFrame(const std::shared_ptr<SpriteFrame>& sf)
	{
		if (!Node::init())
		{
			return false;
		}
	
		if (!sf)
		{
			return false;
		}
	
		this->setSpriteFrame(sf);
	
		return true;
	}
	
	bool Sprite::initWithTexture(const std::shared_ptr<Texture2D>& tex, const Rect& rect)
	{
		if (!Node::init())
		{
			return false;
		}
	
		if (!tex)
		{
			return false;
		}
	
		m_texture = tex;
	
		this->setTextureCoords(rect, &m_quad);
	
		auto r = m_texture->getContentRect();
	
		//float half_w = r.size.width / 2;
		//float half_h = r.size.height / 2;
	
		this->setVertexCoords(Rect(0, 0, r.size.width, r.size.height), &m_quad);
	
		this->updateRenderResource();
	
		return true;
	}
	
	DirectX::XMMATRIX GetWorldMatrix(float x, float y)
	{
		DirectX::XMMATRIX translation = DirectX::XMMatrixTranslation(x, y, 0.0f);
		DirectX::XMMATRIX rotationZ = DirectX::XMMatrixRotationZ(0);
		DirectX::XMMATRIX scale = DirectX::XMMatrixScaling(1.0f, 1.0f, 1.0f);
	
		return translation * rotationZ* scale;
	}
	
	void Sprite::draw(IRender* render, const Matrix4X4& transform)
	{
		DirectX::XMMATRIX mvp = DirectX::XMMatrixMultiply(transform, m_camera->getViewProjectMat());
		m_cmd->updataMat(mvp);
		render->addRenderCommand(m_cmd.get());
	}
	
	
	void Sprite::setFlipH(bool flip /*= false*/)
	{
		if (flip == m_isFlipH)
		{
			return;
		}
	
		m_isFlipH = flip;
	
		this->updateFilp();
	}
	
	void Sprite::setFlipV(bool flip /*= false*/)
	{
		if (flip == m_isFlipV)
		{
			return;
		}
	
		m_isFlipV = flip;
	
		this->updateFilp();
	}
	
	void Sprite::setTexture(const std::shared_ptr<Texture2D>& texture)
	{
		if (texture == nullptr)
		{
			return;
		}
	
		if (m_texture == texture)
		{
			return;
		}
	
		m_texture = texture;
	
		return;
	}
	
	void Sprite::updateFilp()
	{
		auto sf = this->getSpriteFrame();
		this->setTextureCoords(sf->getRect(), &m_quad);
		this->updateRenderResource();
	}
	
	void Sprite::setSpriteFrame(const SpriteFrameSharedPtr& sprite_frame)
	{
		if (sprite_frame == nullptr)
		{
			DebugWarning("精灵帧为空");
			return;
		}
		//DebugWarning("set frame ");
		if (m_spriteFrame != sprite_frame)
		{
			m_spriteFrame = sprite_frame;
		}
	
		auto  texture = sprite_frame->getTexture().lock();
		if (texture != nullptr)
		{
			this->setTexture(texture);
	
			auto r = sprite_frame->getRect();
			this->setTextureCoords(r, &m_quad);
	
			this->setVertexCoords(Rect(0, 0, r.size.width, r.size.height), &m_quad);
	
			this->updateRenderResource();
		}
		else
		{
			DebugWarning("精灵帧的纹理为null");
		}
		return;
	}
	
	SpriteFrameSharedPtr Sprite::getSpriteFrame() const
	{
		if (m_spriteFrame && m_spriteFrame->getTexture().lock() == m_texture)
		{
			return m_spriteFrame;
		}
		//TODO 没有指定矩形区域 
		auto sf = SpriteFrame::createFromTexture(m_texture, Rect());
		if (sf)
		{
			m_spriteFrame = sf;
			return m_spriteFrame;
		}
	
		return nullptr;
	}
	
	void Sprite::setAnchorPoint(const Vector2& anchor)
	{
		if (anchor == m_anchorPoint)
		{
			return;
		}
	
		auto s = getSpriteFrame();
		if (s)
		{
			auto r = s->getRect();
			this->setVertexCoords(Rect(0, 0, r.size.width, r.size.height), &m_quad);
			this->updateRenderResource();
		}
	}
	
	bool Sprite::updateRenderResource()
	{
	
		auto s = ProgramStateCache::getInstance();
	
		if (m_material == nullptr)
		{
			m_material = std::make_shared<Material>(
				m_texture,
				s->getBlendState()->NonPremultiplied(),
				s->getSamplerState()->LinearClamp(),
				s->getRasterizerState()->CullCounterClockwise(),
				s->getDepthStencilState()->DepthNone(),
				ProgramCache::getInstance()->getProgramByName(ProgramCache::SHADER_POSITION_TEXTURE)
				);
		}
		else
		{
			m_material->texture = m_texture;
		}
	
		if (m_cmd == nullptr)
		{
			m_cmd = std::make_shared<TrianglesCommand>();
		}
	
		m_cmd->init(
			m_material,
			DirectX::XMMatrixMultiply(m_transformMat, m_camera->getViewProjectMat()),
			std::vector<VertexPositionTexture>{
				{ m_quad.topRight.position, m_quad.topRight.tex },
				{ m_quad.bottomRight.position,m_quad.bottomRight.tex },
				{ m_quad.bottomLeft.position,m_quad.bottomLeft.tex },
				{ m_quad.topLeft.position,m_quad.topLeft.tex },
		},
			std::vector<UINT>{
					0, 1, 2,
						0, 2, 3
				}
				);
	
	
		return true;
	}
	
	void Sprite::setTextureCoords(const Rect& rect, V2C4T2_Quad* out_quad)
	{
		if (m_texture == nullptr)
		{
			return;
		}
	
		auto& textSize = m_texture->getContentSize();
		const float texWidth = textSize.width;
		const float texHeight = textSize.height;
	
		float rectWidth = rect.size.width;
		float rectHeight = rect.size.height;
	
		float left = 0;
		float right = 0;
		float bottom = 0;
		float top = 0;
	
		if (m_isFlipV)
		{
			right = rect.origin.x / texWidth;
			left = (rect.origin.x + rectWidth) / texWidth;
		}
		else
		{
			left = rect.origin.x / texWidth;
			right = (rect.origin.x + rectWidth) / texWidth;
		}
	
		if (m_isFlipH)
		{
			top = 1 - rect.origin.y / texHeight;
			bottom = 1 - (rect.origin.y + rectHeight) / texHeight;
		}
		else
		{
			bottom = 1 - rect.origin.y / texHeight;
			top = 1 - (rect.origin.y + rectHeight) / texHeight;
		}
	
		out_quad->bottomLeft.tex = Vector2(left, bottom);
		out_quad->bottomRight.tex = Vector2(right, bottom);
		out_quad->topLeft.tex = Vector2(left, top);
		out_quad->topRight.tex = Vector2(right, top);
	}
	
	void Sprite::setVertexCoords(const Rect & sprite_rect, V2C4T2_Quad * out_quad)
	{
		//计算锚点的偏移
		float off_x = sprite_rect.size.width * m_anchorPoint.x;
		float off_y = sprite_rect.size.height * m_anchorPoint.y;
		//左下角的坐标
		float x = sprite_rect.origin.x - off_x;
		float y = sprite_rect.origin.y - off_y;
	
		//尺寸
		float w = sprite_rect.size.width;
		float h = sprite_rect.size.height;
	
		out_quad->bottomLeft.position = Vector3(x, y, 1.0f);
		out_quad->bottomRight.position = Vector3(x + w, y, 1.0f);
		out_quad->topLeft.position = Vector3(x, y + h, 1.0f);
		out_quad->topRight.position = Vector3(x + w, y + h, 1.0f);
	
		//this->initGeometry(sprite_rect);
	}
}
